iT邦幫忙

2021 iThome 鐵人賽

DAY 15
0
Modern Web

後疫情時代的 WebRTC 微學習系列 第 15

Day15 [實作] 使用 Socket.io 建立聊天室

  • 分享至 

  • xImage
  •  

實作

  1. 聊天室 server 端,使用 SSL
  2. 聊天室 client 端,使用 SSL

為什麼要使用 Socket.io

  1. 使用webSocket
    1. 建立在 TCP 協議之上
    2. 資料格式比較輕量
    3. 可以發送文字,也可以發送二進位資料
  2. 有房間的概念
  3. 跨平台(瀏覽器/IOS/Android/…),跨語言

伺服器端實作

  1. 建立專案

    cd Desktop/WebRTC/sample
    mkdir socketio-chartroom
    cd socketio-chartroom
    touch server.js
    npm init --yes
    npm install express socket.io
    

    https://ithelp.ithome.com.tw/upload/images/20210929/20130062iPa9G6k5HR.png

  2. 產生本地端開發用SSL憑證

    https://ithelp.ithome.com.tw/upload/images/20210929/20130062tfUNTSEBjM.png

    1. 安裝 mkcert
    2. 開啟終端機 cd 到專案下
    3. mkcert -install
    4. mkcert 0.0.0.0 localhost 127.0.0.1 ::1
    5. mv 0.0.0.0+3-key.pem key.pem & mv 0.0.0.0+3.pem cert.pem
  3. 在 server.js 加入以下程式碼

    'use strict'
    
    const https = require('https')
    const fs = require('fs')
    const express = require('express')
    const socketIo = require('socket.io')
    
    const app = express()
    
    const options = {
      key: fs.readFileSync('key.pem'),
      cert: fs.readFileSync('cert.pem'),
    }
    
    const https_server = https.createServer(options, app)
    https_server.listen(4443, '0.0.0.0')
    
    const io = socketIo(https_server, {
      cors: true,
    })
    
    // 收到使用者連線
    io.sockets.on('connection', (socket) => {
      console.log(socket.id, '已連線')
    
      socket.on('message', (room, data) => {
        io.in(room).emit('message', room, data)
      })
    
      socket.on('join', (room) => {
        socket.join(room)
        socket.emit('joined', room, socket.id)
      })
    
      socket.on('leave', (room) => {
        socket.leave(room)
        socket.to(room).emit('bye', room, socket.id)
        socket.emit('leave', room, socket.id)
      })
    })
    

使用者端實作

  1. 在專案內新增一個 client 資料夾及以下結構

    client
    ├── index.html
    └── js
        └── client.js
    
  2. index.html 中加入

    <html>
      <head>
        <title>Chat Room</title>
      </head>
      <body>
        <table align="center">
          <h1 style="text-align: center">聊天室</h1>
          <tr>
            <td>
              <label for="username">UserName: </label>
              <input type="text" id="username" />
            </td>
          </tr>
          <tr>
            <td>
              <label for="room">room: </label><input type="text" id="room" />
              <button id="connect">Connect</button>
              <button id="leave" disabled>Leave</button>
            </td>
          </tr>
          <tr>
            <td>
              <label for="output">Content: </label><br />
              <textarea disabled style="line-height: 1.5" id="output" rows="10" cols="100"></textarea>
            </td>
          </tr>
          <tr>
            <td>
              <label for="input">Input: </label><br />
              <textarea disabled id="input" rows="3" cols="100"></textarea>
            </td>
          </tr>
          <tr>
            <td>
              <button id="send">Send</button>
            </td>
          </tr>
        </table>
    
        <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
        <script src="./js/client.js"></script>
      </body>
    </html>
    
  3. client.js 中加入

    'use strict'
    
    const userName = document.querySelector('input#username')
    const inputRoom = document.querySelector('input#room')
    const btnConnect = document.querySelector('button#connect')
    const btnLeave = document.querySelector('button#leave')
    const outputArea = document.querySelector('textarea#output')
    const inputArea = document.querySelector('textarea#input')
    const btnSend = document.querySelector('button#send')
    
    let socket
    let room
    
    btnConnect.onclick = () => {
      socket = io('wss://localhost:4443')
    
      socket.on('joined', (room, id) => {
        btnConnect.disabled = true
        btnLeave.disabled = false
        inputArea.disabled = false
        btnSend.disabled = false
      })
    
      socket.on('leave', (room, id) => {
        btnConnect.disabled = false
        btnLeave.disabled = true
        inputArea.disabled = true
        btnSend.disabled = true
    
        socket.disconnect()
      })
    
      socket.on('message', (room, data) => {
        console.log(room, data)
        outputArea.scrollTop = outputArea.scrollHeight
        outputArea.value = outputArea.value + data + '\n'
      })
    
      socket.on('disconnect', (reason) => {
        btnConnect.disabled = false
        btnLeave.disabled = true
        inputArea.disabled = true
        btnSend.disabled = true
      })
    
      room = inputRoom.value
      socket.emit('join', room)
    }
    
    btnSend.onclick = () => {
      let data = inputArea.value
      data = userName.value + ':' + data
      socket.emit('message', room, data)
      inputArea.value = ''
    }
    
    btnLeave.onclick = () => {
      room = inputRoom.value
      socket.emit('leave', room)
    }
    
    inputArea.onkeypress = (event) => {
      if (event.keyCode === 13) {
        let data = inputArea.value
        data = userName.value + ':' + data
        socket.emit('message', room, data)
        inputArea.value = ''
        event.preventDefault()
      }
    }
    

啟動服務

  1. 啟動 server

    開啟終端機,cd 進入專案,執行 node server.js

    https://ithelp.ithome.com.tw/upload/images/20210929/20130062kIEx9td177.png

  2. 啟動使用者端

    開啟另一個終端機,cd 進入專案的 client 資料夾內,執行

    http-server -S -C ../cert.pem -K ../key.pem -o
    

    https://ithelp.ithome.com.tw/upload/images/20210929/20130062ggrPyDYAwY.png


上一篇
Day14 [實作] 分享畫面及錄影
下一篇
Day16 RTCPeerConnection: Offer / Answer
系列文
後疫情時代的 WebRTC 微學習30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
trombone513
iT邦新手 5 級 ‧ 2022-01-18 15:40:54

有考慮使用 webrtc 的 data channel 來做聊天室看看嗎 ?

我要留言

立即登入留言